package com.jeremyfeinstein.slidingmenu.lib;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

import com.jeremyfeinstein.slidingmenu.lib.SlidingMenu.CanvasTransformer;

public class CustomViewBehind extends ViewGroup {

    private static final String TAG = "CustomViewBehind";

    //��Ե�������ٽ�ֵ
    private static final int MARGIN_THRESHOLD = 48; // dips

    //��ʼ��������ģʽ
    private int mTouchMode = SlidingMenu.TOUCHMODE_MARGIN;

    //�����Ϸ���ͼ
    private CustomViewAbove mViewAbove;

    //����������ͼ
    private View mContent;
    private View mSecondaryContent;

    //���廬����Ե���ٽ�ֵ
    private int mMarginThreshold;

    //��ȵ�ƫ����
    private int mWidthOffset;

    private CanvasTransformer mTransformer;

    //�Ƿ��ܹ�ʹ������ͼ
    private boolean mChildrenEnabled;

    public CustomViewBehind(Context context) {
        this(context, null);
    }

    public CustomViewBehind(Context context, AttributeSet attrs) {
        super(context, attrs);
        mMarginThreshold = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                MARGIN_THRESHOLD, getResources().getDisplayMetrics());
    }

    public void setCustomViewAbove(CustomViewAbove customViewAbove) {
        mViewAbove = customViewAbove;
    }

    public void setCanvasTransformer(CanvasTransformer t) {
        mTransformer = t;
    }

    /**
     * ���ÿ�ȵ�ƫ����
     */
    public void setWidthOffset(int i) {
        mWidthOffset = i;
        requestLayout();
    }

    /**
     * ���ñ�Ե�������ٽ�ֵ
     */
    public void setMarginThreshold(int marginThreshold) {
        mMarginThreshold = marginThreshold;
    }

    /**
     * �õ���Ե�������ٽ�ֵ
     */
    public int getMarginThreshold() {
        return mMarginThreshold;
    }

    /**
     * �õ���ͼ�Ŀ��
     */
    public int getBehindWidth() {
        return mContent.getWidth();
    }

    /**
     * ������ͼ������
     */
    public void setContent(View v) {
        if (mContent != null)
            removeView(mContent);
        mContent = v;
        addView(mContent);
    }

    /**
     * �õ���ͼ������
     */
    public View getContent() {
        return mContent;
    }

    /**
     * �����ұ߻����˵������ݣ���ģʽ����ΪLEFT_RIGHTģʽʱ
     */
    public void setSecondaryContent(View v) {
        if (mSecondaryContent != null)
            removeView(mSecondaryContent);
        mSecondaryContent = v;
        addView(mSecondaryContent);
    }

    /**
     * �õ��ұ߻����˵�������
     */
    public View getSecondaryContent() {
        return mSecondaryContent;
    }

    /**
     * �����Ƿ��ܹ�ʹ������ͼ
     */
    public void setChildrenEnabled(boolean enabled) {
        mChildrenEnabled = enabled;
    }

    @Override
    public void scrollTo(int x, int y) {
        super.scrollTo(x, y);
        if (mTransformer != null)
            invalidate();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {
        return !mChildrenEnabled;
    }

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        return !mChildrenEnabled;
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        if (mTransformer != null) {
            canvas.save();
            mTransformer.transformCanvas(canvas, mViewAbove.getPercentOpen());
            super.dispatchDraw(canvas);
            canvas.restore();
        } else
            super.dispatchDraw(canvas);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        final int width = r - l;
        final int height = b - t;
        mContent.layout(0, 0, width - mWidthOffset, height);
        if (mSecondaryContent != null)
            mSecondaryContent.layout(0, 0, width - mWidthOffset, height);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = getDefaultSize(0, widthMeasureSpec);
        int height = getDefaultSize(0, heightMeasureSpec);
        setMeasuredDimension(width, height);
        final int contentWidth = getChildMeasureSpec(widthMeasureSpec, 0, width - mWidthOffset);
        final int contentHeight = getChildMeasureSpec(heightMeasureSpec, 0, height);
        mContent.measure(contentWidth, contentHeight);
        if (mSecondaryContent != null)
            mSecondaryContent.measure(contentWidth, contentHeight);
    }

    //����ģʽ��ֵ
    private int mMode;

    //�Ƿ��ܹ�ʹ�ý��뽥��Ч��
    private boolean mFadeEnabled;

    //���彥�뽥����ֵ
    private float mFadeDegree;

    //���彥�뽥��Ч������
    private final Paint mFadePaint = new Paint();

    //���廬�����ŵ�ֵ
    private float mScrollScale;

    //���廬���˵�����Ӱ
    private Drawable mShadowDrawable;

    //�����ұ߻����˵�����ӰͼƬ
    private Drawable mSecondaryShadowDrawable;

    //������Ӱ�Ŀ��
    private int mShadowWidth;

    /**
     * ����ģʽ��ֵ
     */
    public void setMode(int mode) {
        if (mode == SlidingMenu.LEFT || mode == SlidingMenu.RIGHT) {
            if (mContent != null)
                mContent.setVisibility(View.VISIBLE);
            if (mSecondaryContent != null)
                mSecondaryContent.setVisibility(View.INVISIBLE);
        }
        mMode = mode;
    }

    /**
     * �õ�ģʽ��ֵ
     */
    public int getMode() {
        return mMode;
    }

    /**
     * ���û������ŵ�ֵ
     */
    public void setScrollScale(float scrollScale) {
        mScrollScale = scrollScale;
    }

    /**
     * �õ��������ŵ�ֵ
     */
    public float getScrollScale() {
        return mScrollScale;
    }

    /**
     * ���û����˵�����Ӱ
     */
    public void setShadowDrawable(Drawable shadow) {
        mShadowDrawable = shadow;
        invalidate();
    }

    /**
     * �����ұ߻����˵�����Ӱ
     */
    public void setSecondaryShadowDrawable(Drawable shadow) {
        mSecondaryShadowDrawable = shadow;
        invalidate();
    }

    /**
     * ������Ӱ�Ŀ��
     */
    public void setShadowWidth(int width) {
        mShadowWidth = width;
        invalidate();
    }

    /**
     * �����ܷ�ʹ�ý��뽥��Ч��
     */
    public void setFadeEnabled(boolean b) {
        mFadeEnabled = b;
    }

    /**
     * ���ý��뽥����ֵ
     */
    public void setFadeDegree(float degree) {
        if (degree > 1.0f || degree < 0.0f)
            throw new IllegalStateException("The BehindFadeDegree must be between 0.0f and 1.0f");
        mFadeDegree = degree;
    }

    /**
     * �õ��˵�ҳ��
     */
    public int getMenuPage(int page) {
        page = (page > 1) ? 2 : ((page < 1) ? 0 : page);
        if (mMode == SlidingMenu.LEFT && page > 1) {
            return 0;
        } else if (mMode == SlidingMenu.RIGHT && page < 1) {
            return 2;
        } else {
            return page;
        }
    }

    /**
     * �����·���ͼ�����λ��
     */
    public void scrollBehindTo(View content, int x, int y) {
        int vis = View.VISIBLE;
        if (mMode == SlidingMenu.LEFT) {
            if (x >= content.getLeft()) vis = View.INVISIBLE;
            scrollTo((int) ((x + getBehindWidth()) * mScrollScale), y);
        } else if (mMode == SlidingMenu.RIGHT) {
            if (x <= content.getLeft()) vis = View.INVISIBLE;
            scrollTo((int) (getBehindWidth() - getWidth() +
                    (x - getBehindWidth()) * mScrollScale), y);
        } else if (mMode == SlidingMenu.LEFT_RIGHT) {
            mContent.setVisibility(x >= content.getLeft() ? View.INVISIBLE : View.VISIBLE);
            mSecondaryContent.setVisibility(x <= content.getLeft() ? View.INVISIBLE : View.VISIBLE);
            vis = x == 0 ? View.INVISIBLE : View.VISIBLE;
            if (x <= content.getLeft()) {
                scrollTo((int) ((x + getBehindWidth()) * mScrollScale), y);
            } else {
                scrollTo((int) (getBehindWidth() - getWidth() +
                        (x - getBehindWidth()) * mScrollScale), y);
            }
        }
        if (vis == View.INVISIBLE)
            Log.v(TAG, "behind INVISIBLE");
        setVisibility(vis);
    }

    /**
     * �õ���߲˵�����ͼ
     */
    public int getMenuLeft(View content, int page) {
        if (mMode == SlidingMenu.LEFT) {
            switch (page) {
                case 0:
                    return content.getLeft() - getBehindWidth();
                case 2:
                    return content.getLeft();
            }
        } else if (mMode == SlidingMenu.RIGHT) {
            switch (page) {
                case 0:
                    return content.getLeft();
                case 2:
                    return content.getLeft() + getBehindWidth();
            }
        } else if (mMode == SlidingMenu.LEFT_RIGHT) {
            switch (page) {
                case 0:
                    return content.getLeft() - getBehindWidth();
                case 2:
                    return content.getLeft() + getBehindWidth();
            }
        }
        return content.getLeft();
    }

    /**
     * �õ���߿���ͼ
     */
    public int getAbsLeftBound(View content) {
        if (mMode == SlidingMenu.LEFT || mMode == SlidingMenu.LEFT_RIGHT) {
            return content.getLeft() - getBehindWidth();
        } else if (mMode == SlidingMenu.RIGHT) {
            return content.getLeft();
        }
        return 0;
    }

    /**
     * �õ��ұ߿���ͼ
     */
    public int getAbsRightBound(View content) {
        if (mMode == SlidingMenu.LEFT) {
            return content.getLeft();
        } else if (mMode == SlidingMenu.RIGHT || mMode == SlidingMenu.LEFT_RIGHT) {
            return content.getLeft() + getBehindWidth();
        }
        return 0;
    }

    /**
     * �Ƿ���������Ļ�ı�Ե
     */
    public boolean marginTouchAllowed(View content, int x) {
        int left = content.getLeft();
        int right = content.getRight();
        if (mMode == SlidingMenu.LEFT) {
            return (x >= left && x <= mMarginThreshold + left);
        } else if (mMode == SlidingMenu.RIGHT) {
            return (x <= right && x >= right - mMarginThreshold);
        } else if (mMode == SlidingMenu.LEFT_RIGHT) {
            return (x >= left && x <= mMarginThreshold + left) ||
                    (x <= right && x >= right - mMarginThreshold);
        }
        return false;
    }

    /**
     * ���ô���ģʽ��ֵ
     */
    public void setTouchMode(int i) {
        mTouchMode = i;
    }

    /**
     * �Ƿ�����ͨ�������򿪻����˵�
     */
    public boolean menuOpenTouchAllowed(View content, int currPage, float x) {
        switch (mTouchMode) {
            case SlidingMenu.TOUCHMODE_FULLSCREEN:
                return true;
            case SlidingMenu.TOUCHMODE_MARGIN:
                return menuTouchInQuickReturn(content, currPage, x);
        }
        return false;
    }

    /**
     * �����˵����ٷ���
     */
    public boolean menuTouchInQuickReturn(View content, int currPage, float x) {
        if (mMode == SlidingMenu.LEFT || (mMode == SlidingMenu.LEFT_RIGHT && currPage == 0)) {
            return x >= content.getLeft();
        } else if (mMode == SlidingMenu.RIGHT || (mMode == SlidingMenu.LEFT_RIGHT && currPage == 2)) {
            return x <= content.getRight();
        }
        return false;
    }

    /**
     * �Ƿ�����رջ����˵�
     */
    public boolean menuClosedSlideAllowed(float dx) {
        if (mMode == SlidingMenu.LEFT) {
            return dx > 0;
        } else if (mMode == SlidingMenu.RIGHT) {
            return dx < 0;
        } else if (mMode == SlidingMenu.LEFT_RIGHT) {
            return true;
        }
        return false;
    }

    /**
     * �Ƿ�����򿪻����˵�
     */
    public boolean menuOpenSlideAllowed(float dx) {
        if (mMode == SlidingMenu.LEFT) {
            return dx < 0;
        } else if (mMode == SlidingMenu.RIGHT) {
            return dx > 0;
        } else if (mMode == SlidingMenu.LEFT_RIGHT) {
            return true;
        }
        return false;
    }

    /**
     * �������˵�����Ӱ
     */
    public void drawShadow(View content, Canvas canvas) {
        if (mShadowDrawable == null || mShadowWidth <= 0) return;
        int left = 0;
        if (mMode == SlidingMenu.LEFT) {
            left = content.getLeft() - mShadowWidth;
        } else if (mMode == SlidingMenu.RIGHT) {
            left = content.getRight();
        } else if (mMode == SlidingMenu.LEFT_RIGHT) {
            if (mSecondaryShadowDrawable != null) {
                left = content.getRight();
                mSecondaryShadowDrawable.setBounds(left, 0, left + mShadowWidth, getHeight());
                mSecondaryShadowDrawable.draw(canvas);
            }
            left = content.getLeft() - mShadowWidth;
        }
        mShadowDrawable.setBounds(left, 0, left + mShadowWidth, getHeight());
        mShadowDrawable.draw(canvas);
    }

    /**
     * �������뽥��Ч��
     */
    public void drawFade(View content, Canvas canvas, float openPercent) {
        if (!mFadeEnabled) return;
        final int alpha = (int) (mFadeDegree * 255 * Math.abs(1 - openPercent));
        mFadePaint.setColor(Color.argb(alpha, 0, 0, 0));
        int left = 0;
        int right = 0;
        if (mMode == SlidingMenu.LEFT) {
            left = content.getLeft() - getBehindWidth();
            right = content.getLeft();
        } else if (mMode == SlidingMenu.RIGHT) {
            left = content.getRight();
            right = content.getRight() + getBehindWidth();
        } else if (mMode == SlidingMenu.LEFT_RIGHT) {
            left = content.getLeft() - getBehindWidth();
            right = content.getLeft();
            canvas.drawRect(left, 0, right, getHeight(), mFadePaint);
            left = content.getRight();
            right = content.getRight() + getBehindWidth();
        }
        canvas.drawRect(left, 0, right, getHeight(), mFadePaint);
    }

    private boolean mSelectorEnabled = true;
    private Bitmap mSelectorDrawable;
    private View mSelectedView;

    public void drawSelector(View content, Canvas canvas, float openPercent) {
        if (!mSelectorEnabled) return;
        if (mSelectorDrawable != null && mSelectedView != null) {
            String tag = (String) mSelectedView.getTag(R.id.selected_view);
            if (tag.equals(TAG + "SelectedView")) {
                canvas.save();
                int left, right, offset;
                offset = (int) (mSelectorDrawable.getWidth() * openPercent);
                if (mMode == SlidingMenu.LEFT) {
                    right = content.getLeft();
                    left = right - offset;
                    canvas.clipRect(left, 0, right, getHeight());
                    canvas.drawBitmap(mSelectorDrawable, left, getSelectorTop(), null);
                } else if (mMode == SlidingMenu.RIGHT) {
                    left = content.getRight();
                    right = left + offset;
                    canvas.clipRect(left, 0, right, getHeight());
                    canvas.drawBitmap(mSelectorDrawable, right - mSelectorDrawable.getWidth(), getSelectorTop(), null);
                }
                canvas.restore();
            }
        }
    }

    public void setSelectorEnabled(boolean b) {
        mSelectorEnabled = b;
    }

    public void setSelectedView(View v) {
        if (mSelectedView != null) {
            mSelectedView.setTag(R.id.selected_view, null);
            mSelectedView = null;
        }
        if (v != null && v.getParent() != null) {
            mSelectedView = v;
            mSelectedView.setTag(R.id.selected_view, TAG + "SelectedView");
            invalidate();
        }
    }

    private int getSelectorTop() {
        int y = mSelectedView.getTop();
        y += (mSelectedView.getHeight() - mSelectorDrawable.getHeight()) / 2;
        return y;
    }

    public void setSelectorBitmap(Bitmap b) {
        mSelectorDrawable = b;
        refreshDrawableState();
    }

}
